package com.zhss.demo.register.server;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class ServiceAliveMonitor {
    /**
     * 检查服务实例是否存活的间隔
     */
    private static final Long CHECK_ALIVE_INTERVAL = 60 * 1000L;

    /**
     * 负责监控微服务存活状态的后台线程
     */
    private Daemon daemon;

    public ServiceAliveMonitor() {
        ThreadGroup daemonThreadGroup = new ThreadGroup("deamon");
        this.daemon = new Daemon(daemonThreadGroup, "ServiceAliveMonitor");
        // 这里将心跳续检测线程设置成deamon线程，此种线程会随着jvm进程结束而结束
        // 如果工作线程（mian线程）都结束，daemon线程是不会阻止jvm进程脱退出的
        // daemon线程会跟jvm进程一起退出
        daemon.setDaemon(true);
    }

    /**
     * 启动后台线程
     */
    public void start() {
        daemon.start();
    }

    /**
     * 负责监控微服务存活状态的后台线程
     * @author zhonghuashishan
     */
    private class Daemon extends Thread {

        // 获取注册表的服务信息，同时也启动了一个线程
        private ServiceRegistry registry = ServiceRegistry.getInstance();

        public Daemon(ThreadGroup threadGroup, String name) {
            super(threadGroup, name);
        }

        @Override
        public void run() {
            Map<String, Map<String, ServiceInstance>> registryMap = null;

            System.out.println(Thread.currentThread().getName() + "线程的线程组是:" + Thread.currentThread().getThreadGroup());
            System.out.println(Thread.currentThread().getName() + "线程组的父线程组是:" + Thread.currentThread().getThreadGroup().getParent());


            while (true) {
                try {
                    // 可以判断一下是否要开启自我保护机制
                    SelfProtectionPolicy selfProtectionPolicy = SelfProtectionPolicy.getInstance();
                    if (selfProtectionPolicy.isEnable()) {
                        Thread.sleep(CHECK_ALIVE_INTERVAL);
                        continue;
                    }
                    // 定义要删除的服务实例的集合
                    List<ServiceInstance> removingServiceInstances = new ArrayList<ServiceInstance>();
                    // 开始对读服务注册表数据，在这个过程中，别人可以读，但是不可以写
                    try {
                        // 这里加上读锁
                        registry.readLock();
                        // 获取整个注册表
                        registryMap = registry.getRegistry();
                        // 获取注册表的服务信息
                        for(String serviceName : registryMap.keySet()) {
                            Map<String, ServiceInstance> serviceInstanceMap = registryMap.get(serviceName);
                            // 判断注册表中中的服务实例是否存活，如果不存活就将这个实例放入待移除的队列中
                            for(ServiceInstance serviceInstance : serviceInstanceMap.values()) {
                                // 说明服务实例距离上一次发送心跳已经超过90秒了
                                // 认为这个服务就死了
                                // 放入待删除的服务实例集合中
                                if(!serviceInstance.isAlive()) {
                                    removingServiceInstances.add(serviceInstance);
                                }
                            }
                        }
                    } finally {
                        // 释放读锁
                        registry.unReadLock();
                    }

                    // 将所有的要删除的服务实例，从服务注册表删除
                    for(ServiceInstance serviceInstance : removingServiceInstances) {
                        // 这里在移除的时候加了些写锁
                        registry.remove(serviceInstance.getServiceName(), serviceInstance.getServiceInstanceId());
                        // 更新自我保护机制的阈值
                        synchronized(SelfProtectionPolicy.class) {
                            selfProtectionPolicy.setExpectedHeartbeatRate(
                                    selfProtectionPolicy.getExpectedHeartbeatRate() - 2);
                            selfProtectionPolicy.setExpectedHeartbeatThreshold(
                                    (long)(selfProtectionPolicy.getExpectedHeartbeatRate() * 0.85));
                        }
                    }
                    // 过期注册表缓存
                    if(removingServiceInstances.size() != 0) {
                        // 过期掉注册表缓存
                        ServiceRegistryCache.getInstance().invalidate();
                    }
                    Thread.sleep(CHECK_ALIVE_INTERVAL);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
